package util;

import exception.ReflectionException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * \* User: MeiZhongHao
 * \* Date: 2018-11-15
 * \* Time: 17:22
 * \* Description:
 * \
 */
public final class ReflectionUtil {

    private static final Logger logger = LogManager.getLogger(ReflectionUtil.class);

    public static <T> T methodInvoker(Object target, String methodName, Object[] args) {
        Object result = null;
        Class cls = target.getClass();
        Class[] argtypes = new Class[0];
        if (args != null) {
            argtypes = new Class[args.length];
            ArrayList invoker = new ArrayList();
            Object[] e = args;
            int argsLen = args.length;

            for (int i = 0; i < argsLen; ++i) {
                Object arg = e[i];
                if (arg != null) {
                    invoker.add(arg.getClass());
                } else {
                    invoker.add((Object) null);
                }
            }

            invoker.toArray(argtypes);
        }

        Method method = getMethod(cls, methodName, argtypes);
        if (method == null) {
            logger.warn("找不到方法{}({})", new Object[]{methodName, argtypes});
            return null;
        } else {
            try {
                result = method.invoke(target, args);
                return (T) result;
            } catch (IllegalArgumentException var11) {
                logger.error("调用方法{}时发生无效参数异常", methodName, var11);
                throw new ReflectionException(var11);
            } catch (IllegalAccessException var12) {
                logger.error("调用方法{}时发生非法调用异常", methodName, var12);
                throw new ReflectionException(var12);
            } catch (InvocationTargetException var13) {
                logger.error("调用方法{}时发生调用异常", methodName, var13);
                throw new ReflectionException(var13.getCause());
            }
        }
    }

    private static Method getMethod(Class<?> target, String methodName, Class<?>[] argTypes) {
        Method method = null;

        try {
            method = target.getDeclaredMethod(methodName, argTypes);
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
        } catch (SecurityException var10) {
            logger.error("获取方法异常", var10);
        } catch (NoSuchMethodException var11) {
            Method[] methods = target.getDeclaredMethods();
            int len = methods.length;

            for (int i = 0; i < len; ++i) {
                Method m = methods[i];
                if (m.getName().equals(methodName) && m.getParameterTypes().length == argTypes.length) {
                    method = m;
                    if (!m.isAccessible()) {
                        m.setAccessible(true);
                    }
                    break;
                }
            }
        }

        if (method == null) {
            Class clazz = target.getSuperclass();

            while (clazz != Object.class) {
                try {
                    method = clazz.getDeclaredMethod(methodName, argTypes);
                    return method;
                } catch (NoSuchMethodException var9) {
                    clazz = clazz.getSuperclass();
                }
            }
        }

        return method;
    }

    public static <T> T getFieldValueByName(String fieldName, Object o) {
        String firstLetter = fieldName.substring(0, 1).toUpperCase();
        String getter = "get" + firstLetter + fieldName.substring(1);

        try {
            Method method = o.getClass().getMethod(getter, new Class[0]);
            Object e = method.invoke(o, new Object[0]);
            return (T) e;
        } catch (Exception var6) {
            logger.error("利用反射获取属性的值发生异常", var6);
            return null;
        }
    }

    public static String[] getFiledName(Object o) {
        Field[] fields = o.getClass().getDeclaredFields();
        String[] fieldNames = new String[fields.length];

        for (int i = 0; i < fields.length; ++i) {
            fieldNames[i] = fields[i].getName();
        }

        return fieldNames;
    }

    public static Object[] getFiledValues(Object o) {
        String[] fieldNames = getFiledName(o);
        Object[] value = new Object[fieldNames.length];

        for (int i = 0; i < fieldNames.length; ++i) {
            value[i] = getFieldValueByName(fieldNames[i], o);
        }

        return value;
    }

}